#include "IElementService.h"

#include <list>
#include <map>
#include <vector>

#include "DataStructure/Input/Element/CQUAD4Data.h"
#include "DataStructure/Input/Element/CTRIA3Data.h"

#include "DBManager/Common/ServiceMacro.h"
#include "DBManager/IRepository/Input/Element/IElementSummaryRepository.h"
#include "DBManager/IRepository/Input/Element/INIdMapEIdsRepository.h"

#include "IP2ECacheService.h"

SG_DBMANAGER_NAMESPACE_OPEN

using namespace SG::DataStructure::FEM;
using namespace SG::DataStructure::Common;

template <>
std::shared_ptr<IElementService> DBServiceFactory::get ()
{
    const size_t typeID = typeid (IElementService).hash_code ();
    auto         iter   = m_ServiceRep.find (typeID);
    if (iter == m_ServiceRep.end ())
    {
        m_ServiceRep[typeID] =
            std::shared_ptr<void> (new IElementService (this), [] (void* p) { delete static_cast<IElementService*> (p); });
    }
    return std::static_pointer_cast<IElementService> (m_ServiceRep[typeID]);
}

IElementService::~IElementService () = default;

IElementService::IElementService (DBServiceFactory* dbServiceFactory) : m_dbm (dbServiceFactory)
{}

DataOperate& IElementService::GetDataOperate ()
{
    return m_dbm->GetDatabase ()->GetDataOperate ();
}

IElementSummaryRepository* IElementService::GetRepository ()
{
    if (m_repository == nullptr)
        m_repository = m_dbm->GetDatabase ()->GetRepository<IElementSummaryRepository> ().get ();
    return m_repository;
}

std::vector<ElementTypes> IElementService::GetAllElementTypes ()
{
    return GetRepository ()->GetAllElementTypes ();
}

ElementTypes IElementService::GetElementType (Id eid)
{
    return GetRepository ()->GetElementType (eid);
}

/// @brief 类型为`type`的单元的数量
std::size_t IElementService::GetElementSize (const ElementTypes type)
{
    return GetRepository ()->GetElementSize (type);
}

/// @brief 获取指定类型的所有单元ID
std::vector<Id> IElementService::GetElementIdsByType (const ElementTypes type)
{
    return GetRepository ()->GetElementIdsByType (type);
}

SG::Algebra::Matrix IElementService::GetElementNodes (SG::DataStructure::FEM::ElementTypes type)
{
    return GetRepository ()->GetElementNodes (type);
}

std::vector<Id> IElementService::FindElementNodes (Id eid)
{
    const ElementTypes type = GetRepository ()->GetElementType (eid);

    // 获取单元类型的处理函数
    auto funMap = m_dbm->GetDatabase ()->GetHub ()->FindElementNodesFunMap ();
    if (funMap.count (type) > 0)
    {
        return funMap[type](eid);
    }

    return std::vector<Id> ();
}

ElementTypes IElementService::FindElement (Id eleId, const std::vector<ElementTypes>& etypes)
{
    SummaryInfo<ElementTypes> entity;
    if (Status::STATUS_Success == GetRepository ()->FindById (eleId, entity))
    {
        return entity.m_type;
    }

    SGError << "Cannot find the element. id:" << eleId;

    return ElementTypes::ELETYPE_Unknown;
}

std::vector<Id> IElementService::GetAllElementId ()
{
    return GetRepository ()->GetAllId ();
}

std::vector<Id> IElementService::GetElementIdByNode (Id nodeId)
{
    // 先检查服务有没有缓存
    std::shared_ptr<INIdMapEIdsRepository> NidMapEidsRepos = m_dbm->GetDatabase ()->GetRepository<INIdMapEIdsRepository> ();
    // 去数据库中检查有没有缓存
    NIdMapEidsData eleids;
    // NidMapEidsRepos->FindById(nodeId, eleids);
    if (NidMapEidsRepos->Size () == 0)
    {
        // 1.开始生成缓存
        std::map<Id, std::vector<Id>> nodeIdMapEleids;

        std::vector<Id> eids = GetAllElementId ();

        for (Id eid : eids)
        {
            std::vector<Id> nids = FindElementNodes (eid);
            for (Id nid : nids)
            {
                std::vector<Id>& eidVec = nodeIdMapEleids[nid];
                eidVec.push_back (eid);
            }
        }
        // 2.将缓存记录到数据库中
        for (auto& it : nodeIdMapEleids)
        {
            NIdMapEidsData data;
            data.m_id         = it.first;
            data.m_elementIds = it.second;
            NidMapEidsRepos->Add (data);
        }
    }

    NidMapEidsRepos->FindById (nodeId, eleids);

    return eleids.m_elementIds;
}

Id IElementService::QueryShellPropId (Id& shellId)
{
    auto eleType = this->FindElement (shellId);
    switch (eleType)
    {
    case ElementTypes::ELETYPE_ShellQuad4: {
        CQUAD4Data eleData;
        FindById (shellId, _OUT eleData);
        return eleData.m_pId;
        break;
    }
    // ToDo 三角形添加 case
    case ElementTypes::ELETYPE_ShellTria3: {
        CTRIA3Data eleData;
        FindById (shellId, _OUT eleData);
        return eleData.m_pId;
        break;
    }

    default:
        SGFatal << "can't find element type \n";
        break;
    }

    return 0;
}

std::vector<Id> IElementService::FindElementByPropId (Id propId)
{
    auto service = m_dbm->get<IP2ECacheService> ();
    if (service->Size () == 0)
    {
        for (auto&& i : m_repository->GetP2EMap ())
        {
            service->Add (i.second);
        }
    }

    // 返回该属性对应的所有单元Id
    TwoLayersData data;
    if (service->FindById (propId, _OUT data) == Status::STATUS_Success)
    {
        return data.m_subs;
    }

    return {};
}

SG_DBMANAGER_NAMESPACE_CLOSE